import crypto from 'crypto';
import jwt from 'jsonwebtoken';
import bcrypt from 'bcryptjs';
import validator from 'validator';
import asyncHandler from 'express-async-handler';
import User from '../models/userModel.js';
import { sendPasswordResetEmail } from '../utils/sendEmail.js';
import mongoose from 'mongoose';
import { OAuth2Client } from 'google-auth-library';

// Create token
/**
 * 创建一个给定用户ID的JSON Web Token (JWT)。
 * @param {string|number} id - 用户的唯一标识符，可以是字符串或数字。
 * @returns {string} 生成的 JWT。
 */
const createToken = (id) => {
  // 使用用户ID生成 JWT，指定过期时间和密钥加密。
  return jwt.sign({ id }, process.env.JWT_SECRET, {
    expiresIn: '30d', // Token 过期时间
  });
};


// Google OAuth2 Client
const client = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);

// Register user
/**
 * 注册用户
 * @param {Object} req - 请求对象，包含用户输入的注册信息
 * @param {Object} res - 响应对象，用于向客户端发送响应
 */
const registerUser = asyncHandler(async (req, res) => {
  // 从请求体中解构用户输入的信息
  const { name, email, password, isAdmin, adminKey } = req.body;
  // 根据是否有文件上传，决定是否存储个人资料图片路径
  const profilePicture = req.file ? req.file.path : null;


  // 获取环境变量中的管理员注册密钥
  const ADMIN_REGISTRATION_KEY = process.env.ADMIN_REGISTRATION_KEY;

  try {
    // 检查邮箱是否已被注册
    const exists = await User.findOne({ email });
    if (exists) {
      return res
        .status(400)
        .json({ success: false, message: 'User already exists' });
    }

    // 验证邮箱格式是否正确
    if (!validator.isEmail(email)) {
      return res
        .status(400)
        .json({ success: false, message: 'Please enter a valid email' });
    }
    // 验证密码长度是否符合要求
    if (password.length < 8) {
      return res
        .status(400)
        .json({ success: false, message: 'Please enter a strong password' });
    }

    // 如果用户尝试注册为管理员，验证管理员注册密钥是否正确
    if (isAdmin && adminKey !== ADMIN_REGISTRATION_KEY) {
      return res
        .status(400)
        .json({ success: false, message: 'Invalid admin registration key' });
    }

    // 生成密码盐，并对密码进行散列处理
    const salt = await bcrypt.genSalt(10);
    const hashedPassword = await bcrypt.hash(password, salt);

    // 创建新的用户实例，并保存到数据库
    const newUser = new User({
      name,
      email,
      profilePicture,
      password: hashedPassword,
      isAdmin,
    });
    const user = await newUser.save();

    // 创建用户认证令牌
    const token = createToken(user._id);

    // 向客户端发送包含用户信息和认证令牌的响应
    res.status(201).json({
      success: true,
      user: {
        _id: user._id,
        name: user.name,
        email: user.email,
        profilePicture: user.profilePicture,
        isAdmin: user.isAdmin,
      },
      token,
    });
  } catch (error) {
    // 处理服务器错误
    console.error(error);
    res.status(500).json({ success: false, message: 'Server error' });
  }
});

// Login user
/**
 * 登录用户
 * 使用asyncHandler处理异步操作，确保错误能够被正确捕获和响应
 * @param {Object} req - 请求对象，包含用户登录信息（email和password）
 * @param {Object} res - 响应对象，用于发送响应给客户端
 */
const loginUser = asyncHandler(async (req, res) => {
  // 从请求体中解构出email和password
  const { email, password } = req.body;

  try {
    // 使用User模型查找与提供的email匹配的用户
    const user = await User.findOne({ email });

    // 如果没有找到用户，返回400状态码和错误信息
    if (!user) {
      return res
        .status(400)
        .json({ success: false, message: 'User does not exist' });
    }

    // 使用bcrypt比较用户输入的password和数据库中的密码
    const isMatch = await bcrypt.compare(password, user.password);

    // 如果密码不匹配，返回400状态码和错误信息
    if (!isMatch) {
      return res
        .status(400)
        .json({ success: false, message: 'Invalid credentials' });
    }

    // 创建JWT令牌
    const token = createToken(user._id);

    // 返回成功响应，包含用户信息和令牌
    res.json({
      success: true,
      user: {
        _id: user._id,
        name: user.name,
        email: user.email,
        isAdmin: user.isAdmin,
      },
      token,
    });
  } catch (error) {
    // 捕获到错误时，记录错误并返回500状态码和错误信息
    console.error(error);
    res.status(500).json({ success: false, message: 'Server error' });
  }
});


// Google login user
/**
 * 使用Google令牌进行登录的异步处理函数
 * 此函数旨在处理Google登录的后端逻辑，包括验证Google ID令牌、用户创建和登录状态的响应
 * @param {Object} req Express请求对象，应包含来自前端的tokenId
 * @param {Object} res Express响应对象，用于发送响应给客户端
 */
const googleLogin = asyncHandler(async (req, res) => {
  // 从请求体中提取tokenId
  const { tokenId } = req.body;

  try {
    // 验证Google ID令牌并获取用户信息
    const ticket = await client.verifyIdToken({
      idToken: tokenId,
      audience: process.env.GOOGLE_CLIENT_ID,
    });

    // 解析验证响应以获取用户信息
    const payload = ticket.getPayload();
    const { sub, email, name } = payload;

    // 尝试查找现有用户
    let user = await User.findOne({ email });
    // 如果用户不存在，则创建新用户
    if (!user) {
      user = new User({ googleId: sub, email, name });
      await user.save();
    }
    // 创建用户登录令牌
    const token = createToken(user._id);
    // 返回成功响应，包括用户信息和登录令牌
    res.status(200).json({
      success: true,
      user: {
        _id: user._id,
        name: user.name,
        email: user.email,
        isAdmin: user.isAdmin,
      },
      token,
    });
  } catch (error) {
    // 捕获并处理错误
    console.error(error);
    res.status(401).json({ success: false, message: 'Invalid token' });
  }
});


// Get user profile
const getUserProfile = asyncHandler(async (req, res) => {
  // 根据请求中的用户ID查找用户
  const user = await User.findById(req.user.id);

  // 如果找到用户，返回用户信息（不包括密码等敏感信息）
  if (user) {
    res.json({
      _id: user._id,
      name: user.name,
      email: user.email,
      isAdmin: user.isAdmin,
    });
  } else {
    // 如果未找到用户，返回404错误和错误信息
    res.status(404).json({ message: 'User not found' });
  }
});


// Update user profile
const updateUserProfile = asyncHandler(async (req, res) => {
  // 根据请求中的用户ID查找用户
  const user = await User.findById(req.user.id);

  // 如果找到用户，则更新用户信息
  if (user) {
    // 更新用户名，如果请求体中没有提供新名字，则保留原名字
    user.name = req.body.name || user.name;
    // 更新用户邮箱，如果请求体中没有提供新邮箱，则保留原邮箱
    user.email = req.body.email || user.email;
    // 如果提供了新密码，则更新密码
    if (req.body.password) {
      // 生成密码盐
      const salt = await bcrypt.genSalt(10);
      // 使用盐对新密码进行哈希处理
      user.password = await bcrypt.hash(req.body.password, salt);
    }

    // 保存更新后的用户信息
    const updatedUser = await user.save();

    // 发送更新后的用户信息给客户端，不包括密码等敏感信息
    res.json({
      _id: updatedUser._id,
      name: updatedUser.name,
      email: updatedUser.email,
      isAdmin: updatedUser.isAdmin,
    });
  } else {
    // 如果未找到用户，返回404错误和错误信息
    res.status(404).json({ message: 'User not found' });
  }
});


// Create user (admin only)
const createUser = asyncHandler(async (req, res) => {
  const { name, email, password, isAdmin } = req.body;

  try {
    // 检查用户是否已存在
    const exists = await User.findOne({ email });
    if (exists) {
      // 如果用户已存在，返回400状态码和错误信息
      return res
        .status(400)
        .json({ success: false, message: 'User already exists' });
    }

    // 验证电子邮件格式
    if (!validator.isEmail(email)) {
      // 如果电子邮件格式不正确，返回400状态码和错误信息
      return res
        .status(400)
        .json({ success: false, message: 'Please enter a valid email' });
    }
    // 验证密码长度
    if (password.length < 8) {
      // 如果密码长度不足，返回400状态码和错误信息
      return res
        .status(400)
        .json({ success: false, message: 'Please enter a strong password' });
    }

    // 生成密码盐
    const salt = await bcrypt.genSalt(10);
    // 使用盐值对密码进行哈希处理
    const hashedPassword = await bcrypt.hash(password, salt);

    // 创建新的用户实例
    const newUser = new User({
      name,
      email,
      password: hashedPassword,
      isAdmin,
    });
    // 保存用户到数据库
    const user = await newUser.save();

    // 创建用户认证令牌
    const token = createToken(user._id);

    // 发送201状态码和用户数据及令牌作为响应
    res.status(201).json({
      success: true,
      user: {
        _id: user._id,
        name: user.name,
        email: user.email,
        isAdmin: user.isAdmin,
      },
      token,
    });
  } catch (error) {
    // 捕获到异常时，记录错误日志并返回500状态码和错误信息
    console.error(error);
    res.status(500).json({ success: false, message: 'Server error' });
  }
});


const updateUser = asyncHandler(async (req, res) => {
  // 从请求参数中提取用户ID
  const { id } = req.params;

  // 根据ID查找用户
  const user = await User.findById(id);

  // 如果找到用户，则进行更新操作
  if (user) {

    // 检查并验证电子邮件是否已更改且新电子邮件是否已存在
    if (req.body.email && req.body.email !== user.email) {
      const emailExists = await User.findOne({ email: req.body.email });
      if (emailExists) {
        // 如果电子邮件已存在，返回400错误
        return res.status(400).json({ message: 'Email already in use' });
      }
    }

    // 更新用户名称和电子邮件，如果未提供新值，则保持原值
    user.name = req.body.name || user.name;
    user.email = req.body.email || user.email;

    // 如果提供了新密码，则更新密码
    if (req.body.password) {
      user.password = req.body.password;
    }

    // 保存更新后的用户信息
    const updatedUser = await user.save();

    // 返回更新后的用户信息，不包括密码
    res.status(200).json({
      _id: updatedUser._id,
      name: updatedUser.name,
      email: updatedUser.email,
      role: updatedUser.role,
    });
  } else {
    // 如果未找到用户，返回404错误并抛出异常
    res.status(404);
    throw new Error('User not found');
  }
});


// Logout user
const logoutUser = async (req, res) => {
  // 清除用户的JWT令牌，通过设置cookie的httpOnly属性和过期时间为0实现
  res.cookie('jwt', '', {
    httpOnly: true,
    expires: new Date(0),
  });

  // 发送200状态码和登出消息作为响应
  res.status(200).json({ message: 'Logout User' });
};


// Delete user (admin only)
const deleteUser = asyncHandler(async (req, res) => {
  // 提取请求参数中的用户ID
  const userId = req.params.id;

  // 验证用户ID的格式是否有效
  if (!mongoose.Types.ObjectId.isValid(userId)) {
    return res.status(400).json({ message: 'Invalid user ID format' });
  }

  // 尝试找到并删除指定ID的用户
  const user = await User.findByIdAndDelete(userId);

  // 根据删除结果返回相应的响应
  if (user) {
    res.json({ success: true, message: 'User removed' });
  } else {
    res.status(404).json({ message: 'User not found' });
  }
});

// Get all users (admin only)
const getUsers = async (req, res) => {
  try {
    // 使用 User 模型查询所有用户信息
    const users = await User.find({});
    // 将查询结果作为 JSON 返回
    res.json({ success: true, data: users });
  } catch (error) {
    // 当发生异常时记录错误信息
    console.error(error);
    // 当发生异常时返回错误响应
    res.status(500).json({ success: false, message: 'Error fetching users' });
  }
};

/**
 * 生成重置密码的令牌
 * @returns {string} 返回一个长度为64字符的字符串，包含随机生成的重置密码令牌
 */
const generateResetToken = () => {
  return crypto.randomBytes(32).toString('hex');
};


/**
 * 处理用户忘记密码的请求
 */
const forgotPassword = asyncHandler(async (req, res) => {
  // 从请求体中提取email
  const { email } = req.body;

  try {
    // 查找与提供的email关联的用户
    const user = await User.findOne({ email });
    // 如果用户不存在，返回404状态码和错误消息
    if (!user) {
      return res.status(404).json({ message: 'User not found' });
    }

    // 生成密码重置令牌
    const resetToken = generateResetToken();
    // 更新用户的重置密码令牌和过期时间
    user.resetPasswordToken = resetToken;
    user.resetPasswordExpires = Date.now() + 3600000; // 1小时有效期
    // 保存用户信息到数据库
    await user.save();

    // 发送包含重置密码链接的邮件
    await sendPasswordResetEmail(user, user.resetPasswordToken);

    // 返回成功响应，包含消息和重置令牌
    res.status(200).json({ message: 'Password reset email sent', resetToken });
  } catch (error) {
    // 捕获到错误时，记录错误并返回500状态码和错误消息
    console.error(error);
    res.status(500).json({ message: 'Internal server error' });
  }
});


/**
 * 重置密码令牌获取函数
**/
const getResetPasswordToken = asyncHandler(async (req, res) => {
  // 从请求参数中提取令牌
  const { token } = req.params;

  // 发送包含重置密码表单的HTML响应
  // 表单包括一个隐藏的令牌输入字段和一个要求输入新密码的字段
  res.send(`
      <form action="/reset-password" method="POST">
        <input type="hidden" name="token" value="${token}" />
        <label for="newPassword">New Password:</label>
        <input type="password" name="newPassword" required />
        <button type="submit">Reset Password</button>
      </form>
    `);
});


/**
 * 异步处理密码重置请求
 */
const resetPassword = asyncHandler(async (req, res) => {
  // 从请求体中提取token和新密码
  const { token, newPassword } = req.body;

  try {
    // 查找符合条件的用户：匹配的重置密码token且token未过期
    const user = await User.findOne({
      resetPasswordToken: token,
      resetPasswordExpires: { $gt: Date.now() },
    });

    // 如果找不到用户，返回错误响应
    if (!user) {
      return res
        .status(400)
        .json({ message: 'Password reset token is invalid or has expired' });
    }

    // 生成密码盐
    const salt = await bcrypt.genSalt(10);
    // 使用盐对新密码进行哈希处理
    user.password = await bcrypt.hash(newPassword, salt);
    // 重置密码后，清除用户的重置密码token和过期时间
    user.resetPasswordToken = undefined;
    user.resetPasswordExpires = undefined;
    // 保存用户的新信息
    await user.save();

    // 返回成功响应
    res.status(200).json({ message: 'Password has been reset' });
  } catch (error) {
    // 捕获到错误时，打印错误日志并返回内部服务器错误响应
    console.error(error);
    res.status(500).json({ message: 'Internal server error' });
  }
});


export {
  loginUser,
  logoutUser,
  registerUser,
  getUserProfile,
  updateUserProfile,
  getUsers,
  forgotPassword,
  getResetPasswordToken,
  resetPassword,
  createUser,
  googleLogin,
  updateUser,
  deleteUser,
};
